//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File: clock.c
//  Programmer: Visual.Wei (2007)
//
//  Description:   management system clock,like HCLK,PCLK..etc.
//

#include <windows.h>
#include <ceddk.h>
#include <oal_io.h>
#include <clock.h>

#define dimof(x)                (sizeof(x)/sizeof(x[0]))
//------------------------------------------------------------------------------

#ifdef DEBUG
#define ZONE_ERROR      DEBUGZONE(0)
#else
#define ZONE_ERROR		1
#endif

//------------------------------------------------------------------------------
static const CLK_CLOCK s_clkSPI_100K_2[] =
    {
        CLK_CLOCK_ARMXOR
    };

static const CLK_CLOCK s_clkHDQ[] =
    {
        CLK_CLOCK_ARMXOR
    };

static const CLK_CLOCK s_clkI2C[] =    {CLK_CLOCK_I2C    };

static const CLK_CLOCK s_clkAC97[] =    { CLK_CLOCK_AC97};

static const CLK_CLOCK s_clkCAM[] = {CLK_CLOCK_CAMIF};


static const CLK_CLOCK s_clkSDMMC[] = {CLK_CLOCK_SDMMC};

static const CLK_CLOCK s_clkHSMMC[] = {CLK_CLOCK_HSMMC};

static const CLK_CLOCK s_clkUART0[] =   {CLK_CLOCK_UART0};
static const CLK_CLOCK s_clkUART1[] =   {CLK_CLOCK_UART1};
static const CLK_CLOCK s_clkUART2[] =    {CLK_CLOCK_UART2};
static const CLK_CLOCK s_clkUART3[] =   {CLK_CLOCK_UART3};

static const CLK_CLOCK s_clkPWM[] = {  CLK_CLOCK_PWM };

static const CLK_CLOCK s_clkHCD[] =    { CLK_CLOCK_USBH ,CLK_CLOCK_USBH_EPLL  };

static const CLK_CLOCK s_clkUFN[] =   {CLK_CLOCK_USBD  };

//------------------------------------------------------------------------------
// Visual: do not forget the modification
static const CLK_DEVICE_CLOCKS s_clkDeviceClocks[] =
    {
        {
            S3C2443_BASE_REG_PA_CLOCK_POWER, dimof(s_clkSPI_100K_2),   s_clkSPI_100K_2
        },

        { S3C2443_BASE_REG_PA_CLOCK_POWER, dimof(s_clkHDQ),   s_clkHDQ   },
        { S3C2443_BASE_REG_PA_CLOCK_POWER,       dimof(s_clkI2C),   s_clkI2C   },
        { S3C2443_BASE_REG_PA_AC97,       dimof(s_clkAC97),   s_clkAC97  },
        { S3C2443_BASE_REG_PA_CAM,       dimof(s_clkCAM),   s_clkCAM   },
        { S3C2443_BASE_REG_PA_HSMMC,      dimof(s_clkHSMMC),  s_clkHSMMC  },
        { S3C2443_BASE_REG_PA_UART0,     dimof(s_clkUART0), s_clkUART0 },
        { S3C2443_BASE_REG_PA_UART1,     dimof(s_clkUART1), s_clkUART1 },
        { S3C2443_BASE_REG_PA_UART2,     dimof(s_clkUART2), s_clkUART2 },
        { S3C2443_BASE_REG_PA_UART3,     dimof(s_clkUART3), s_clkUART3 },
        { S3C2443_BASE_REG_PA_PWM,       dimof(s_clkPWM),   s_clkPWM  },
        { S3C2443_BASE_REG_PA_USB,      dimof(s_clkHCD),   s_clkHCD   },
        { S3C2443_BASE_REG_PA_USBD,      dimof(s_clkUFN),   s_clkUFN   },
        { S3C2443_BASE_REG_PA_SDI, dimof(s_clkSDMMC),   s_clkSDMMC   },
    };

//------------------------------------------------------------------------------

S3c2443Clock::S3c2443Clock()
{
	PHYSICAL_ADDRESS pa;

	// Map CLKM registers
	pa.QuadPart = S3C2443_BASE_REG_PA_CLOCK_POWER;
	m_pCLKMRegs = (S3C2443_CLKPWR_REG *)MmMapIoSpace(
	                  pa, sizeof(S3C2443_CLKPWR_REG), FALSE
	              );

	memset(&m_ClockRef, 0, sizeof(m_ClockRef));
	//Visual:
	RETAILMSG(1,(TEXT("RootBus.DLL: Set Default Clock Tree\r\n")));
	//disable CAMIF,STN,USBHOST,USBDEV,CFC,SSMC clock
	CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<8)|(1<<10)|(1<<11)|(1<<12)|(1<<17)|(1<<18));
	//disable SPI_HS,I2S,WDT,SPI_0,SPI_1 clock
	CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<6)|(1<<9)|(1<<11)|(1<<14)|(1<<15));
	//disable USBHOST,I2S,DISPLAY,CAM,HSSPI,SSMMC,DDR clock
	CLRREG32(&m_pCLKMRegs->SCLKCON, (1<<1)|(1<<9)|(1<<10)|(1<<11)|(1<<14)|(1<<15)|(1<<16));
};
//------------------------------------------------------------------------------
BOOL S3c2443Clock::PowerUp()
{
	RETAILMSG(1,(TEXT("RootBus.DLL: Restore Clock Tree\r\n")));
	if(m_pCLKMRegs)
	{
		//hclock=INREG32(&m_pCLKMRegs->HCLKCON);
		//pclock=INREG32(&m_pCLKMRegs->PCLKCON);
		//sclock=INREG32(&m_pCLKMRegs->SCLKCON);
		//disable CAMIF,STN,USBHOST,USBDEV,CFC,SSMC clock
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<8)|(1<<10)|(1<<11)|(1<<12)|(1<<17)|(1<<18));
		//disable SPI_HS,I2S,WDT,SPI_0,SPI_1 clock
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<6)|(1<<9)|(1<<11)|(1<<14)|(1<<15));
		//disable USBHOST,I2S,DISPLAY,CAM,HSSPI,SSMMC,DDR clock
		CLRREG32(&m_pCLKMRegs->SCLKCON, (1<<1)|(1<<9)|(1<<10)|(1<<11)|(1<<14)|(1<<15)|(1<<16));
	}
	return TRUE;
}
//------------------------------------------------------------------------------
BOOL S3c2443Clock::PowerDown()
{
#if 0
	RETAILMSG(1,(TEXT("RootBus.DLL: Backup Clock Tree\r\n")));
	if(m_pCLKMRegs)
	{
		OUTREG32(&m_pCLKMRegs->HCLKCON,hclock);
		OUTREG32(&m_pCLKMRegs->PCLKCON,pclock);
		OUTREG32(&m_pCLKMRegs->SCLKCON,sclock);
	}
#endif	
	return TRUE;
}
//------------------------------------------------------------------------------

S3c2443Clock::~S3c2443Clock()
{
	m_CS.Lock();

	// Unmap CLKM registers
	if (m_pCLKMRegs != NULL)
		MmUnmapIoSpace(
		    (VOID*)m_pCLKMRegs, sizeof(S3C2443_CLKPWR_REG)
		);

	m_CS.Unlock();
};

//------------------------------------------------------------------------------

CLK_CLOCK S3c2443Clock::AllocateClock(
    DEVICE_LOCATION *pDevLoc, CLOCK_ALLOCATION clockId
)
{
	m_CS.Lock();
	CLK_CLOCK allocatedClock = CLK_CLOCK_INVALID ;

	// First make sure device is on internal bus 0

	if (pDevLoc->IfcType != Internal || pDevLoc->BusNumber != 0)
	{
		RETAILMSG(ZONE_ERROR, (L"ERROR: S3c2443Clock::AllocateClock: "
		                      L"Unsupported device interface type (%d) or bus number (%d)\r\n",
		                      pDevLoc->IfcType, pDevLoc->BusNumber
		                     ));
	}

	// Find device info
	for (int ix = 0; ix < dimof(s_clkDeviceClocks); ix++)
	{
		if (pDevLoc->LogicalLoc == s_clkDeviceClocks[ix].base)
		{
			if ((DWORD)clockId < s_clkDeviceClocks[ix].count )
				allocatedClock = s_clkDeviceClocks[ix].pClockId[(DWORD)clockId];

			break;
		}
	}

	m_CS.Unlock();
	return allocatedClock;
}

//------------------------------------------------------------------------------

BOOL S3c2443Clock::FreeClock(CLK_CLOCK clockItem)
{
	BOOL bReturn = FALSE;

	m_CS.Lock();

	if (clockItem < CLK_CLOCK_SIZE)
	{
		bReturn = TRUE;
	}

	ASSERT(bReturn == TRUE);
	m_CS.Unlock();
	return bReturn;
}

//------------------------------------------------------------------------------

BOOL S3c2443Clock::RequestClock(CLK_CLOCK id)
{
	BOOL bReturn = FALSE;

	m_CS.Lock();

	if (id < CLK_CLOCK_SIZE)
	{
		if (m_ClockRef[id] == 0  && id != CLK_CLOCK_INVALID )
		{ // First Time;
			EnableClock(id);
		}

		bReturn = TRUE;
		m_ClockRef[id] ++ ;
	}

	m_CS.Unlock();
	ASSERT(bReturn == TRUE);
	return bReturn;
}

//------------------------------------------------------------------------------

BOOL S3c2443Clock::ReleaseClock( CLK_CLOCK id)
{
	BOOL bReturn = FALSE;

	m_CS.Lock();

	if (id < CLK_CLOCK_SIZE && id != CLK_CLOCK_INVALID)
	{
		if (m_ClockRef[id] > 1)
		{
			m_ClockRef[id]--;
		}
		else
		{
			DisableClock(id);
			m_ClockRef[id] = 0;
		}

		bReturn = TRUE;
	}

	m_CS.Unlock();
	ASSERT(bReturn == TRUE);
	return bReturn;
}

//------------------------------------------------------------------------------
//
//  Function:  EnableClock
//
//  Note: On final code we probably want make it more effecient.
//
BOOL S3c2443Clock::EnableClock(CLK_CLOCK id)
{
	BOOL rc = TRUE;


	RETAILMSG(1,(TEXT("RootBus.DLL EnableClock %d\r\n"),id));
	switch (id)
	{
	//HCLK
	case CLK_CLOCK_HSMMC:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<16));
		SETREG32(&m_pCLKMRegs->SCLKCON, (1<<12)|(1<<13));
		break;
	case CLK_CLOCK_CF:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<17));
		break;	
	case CLK_CLOCK_USBD:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<12));
		break;
	case CLK_CLOCK_USBH:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<11));
		break;	
	case CLK_CLOCK_CAMIF:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<8));
		break;		
	case CLK_CLOCK_LCD:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<9)|(1<<10));
		SETREG32(&m_pCLKMRegs->SCLKCON, (1<<10));
		break;	
	case CLK_CLOCK_DMA0:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<0));
		break;		
	case CLK_CLOCK_DMA1:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<1));
		break;
	case CLK_CLOCK_DMA2:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<2));
		break;
	case CLK_CLOCK_DMA3:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<3));
		break;
	case CLK_CLOCK_DMA4:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<4));
		break;
	case CLK_CLOCK_DMA5:
		SETREG32(&m_pCLKMRegs->HCLKCON, (1<<5));		
	//PCLK
	case CLK_CLOCK_SPI1:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<15));
		break;		
	case CLK_CLOCK_SPI0:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<14));
		break;		
	case CLK_CLOCK_GPIO:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<13));
		break;		
	case CLK_CLOCK_RTC:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<12));
		break;			
	case CLK_CLOCK_WDT:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<11));
		break;		
	case CLK_CLOCK_PWM:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<10));
		break;		
	case CLK_CLOCK_I2S:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<9));
		break;		
	case CLK_CLOCK_AC97:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<8));
		break;		
	case CLK_CLOCK_TSADC:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<7));
		break;		
	case CLK_CLOCK_SPIHS:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<6));
		break;		
	case CLK_CLOCK_SDMMC:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<5));
		break;	
	case CLK_CLOCK_I2C:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<4));
		break;		
	case CLK_CLOCK_UART3:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<3));
		break;	
	case CLK_CLOCK_UART2:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<2));
		break;
	case CLK_CLOCK_UART1:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<1));
		break;
	case CLK_CLOCK_UART0:
		SETREG32(&m_pCLKMRegs->PCLKCON, (1<<0));
		break;		
	//EPLL
	case CLK_CLOCK_USBH_EPLL:
		SETREG32(&m_pCLKMRegs->SCLKCON, (1<<1));
		break;
	default:
		rc = FALSE;
		break;
	}


	return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  DisableClock
//
//  Note: On final code we probably want make it more effecient.
//
BOOL S3c2443Clock::DisableClock(CLK_CLOCK id)
{
	BOOL rc = TRUE;
	RETAILMSG(1,(TEXT("RootBus.DLL DisableClock %d\r\n"),id));
	switch (id)
	{
	//HCLK
	case CLK_CLOCK_HSMMC:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<16));
		CLRREG32(&m_pCLKMRegs->SCLKCON, (1<<12)|(1<<13));	
		break;
	case CLK_CLOCK_CF:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<17));
		break;	
	case CLK_CLOCK_USBD:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<12));
		break;
	case CLK_CLOCK_USBH:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<11));
		break;	
	case CLK_CLOCK_CAMIF:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<8));
		break;		
	case CLK_CLOCK_LCD:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<9)|(1<<10));
		CLRREG32(&m_pCLKMRegs->SCLKCON, (1<<10));
		break;		
	case CLK_CLOCK_DMA0:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<0));
		break;		
	case CLK_CLOCK_DMA1:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<1));
		break;
	case CLK_CLOCK_DMA2:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<2));
		break;
	case CLK_CLOCK_DMA3:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<3));
		break;
	case CLK_CLOCK_DMA4:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<4));
		break;
	case CLK_CLOCK_DMA5:
		CLRREG32(&m_pCLKMRegs->HCLKCON, (1<<5));
		break;		
	//PCLK
	case CLK_CLOCK_SPI1:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<15));
		break;		
	case CLK_CLOCK_SPI0:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<14));
		break;		
	case CLK_CLOCK_GPIO:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<13));
		break;		
	case CLK_CLOCK_RTC:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<12));
		break;			
	case CLK_CLOCK_WDT:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<11));
		break;		
	case CLK_CLOCK_PWM:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<10));
		break;		
	case CLK_CLOCK_I2S:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<9));
		break;		
	case CLK_CLOCK_AC97:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<8));
		break;		
	case CLK_CLOCK_TSADC:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<7));
		break;		
	case CLK_CLOCK_SPIHS:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<6));
		break;		
	case CLK_CLOCK_SDMMC:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<5));
		break;	
	case CLK_CLOCK_I2C:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<4));
		break;		
	case CLK_CLOCK_UART3:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<3));
		break;	
	case CLK_CLOCK_UART2:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<2));
		break;
	case CLK_CLOCK_UART1:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<1));
		break;
	case CLK_CLOCK_UART0:
		CLRREG32(&m_pCLKMRegs->PCLKCON, (1<<0));
		break;		
	//EPLL
	case CLK_CLOCK_USBH_EPLL:
		CLRREG32(&m_pCLKMRegs->SCLKCON, (1<<1));
		break;
	default:
		rc = FALSE;
		break;
	}
	return rc;
}

//------------------------------------------------------------------------------
